package com.fosu.Utils;

import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.session.Configuration;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Desription
 * 注解IN查询支持实现类
 * @author manno
 * @date 2020/10/21
 * SQL节点解析帮助类-XMLLanguageDriver
 * Mybatis默认XML驱动类为XMLLanguageDriver，
 * 其主要作用于解析select|update|insert|delete节点为完整的SQL语句。
 * Pattern类:用于编译正则表达式后创建一个匹配模式。
 * Pattern complie(String regex)：编译正则表达式，并创建Pattern类。
 * String pattern()：返回正则表达式的字符串形式。
 *
 *         其实就是返回Pattern.complile(Stringregex)的regex参数。示例如下：
 *
 *         String regex ="\\?|\\*";
 *
 *         Pattern pattern= Pattern.compile(regex);
 *
 *         StringpatternStr = pattern.pattern();//返回\?\*
 *
 * Pattern.matcher(CharSequenceinput)返回一个Matcher对象。
 * Matcher类的构造方法也是私有的，不能随意创建，
 * 只能通过Pattern.matcher(CharSequence input)方法得到该类的实例。
 * Pattern类只能做一些简单的匹配操作，要想得到更强更便捷的正则匹配操作，
 * 那就需要将Pattern与Matcher一起合作。
 * Matcher类提供了对正则表达式的分组支持，以及对正则表达式的多次匹配支持。
 * String replaceAll(Stringreplacement)：将目标字符串里与既有模式相匹配的子串全部替换为指定的字符串。
 */

public class SimpleSelectInExtendedLanguageDriver
        extends XMLLanguageDriver implements LanguageDriver
{

    /**定义匹配的格式为(#{多种字符})
     * 正则表达式中转义字符为'\'
     * 此处连续使用两个'\'的原因为需要传入正则表达式的字符串形式*/
    private final Pattern inPattern = Pattern.compile("\\(#\\{(\\w+)\\}\\)");

    /**截获获得的script字串并进行相应转换后交由父类继续解析*/
    @Override
    public SqlSource createSqlSource(Configuration configuration,
                                     String script,
                                     Class<?> parameterType)
    {
        /**绑定匹配格式*/
        Matcher matcher = inPattern.matcher(script);
        /**将原SQL中与模式匹配的部分全部改写成使用foreach逐个比对#{}内的元素的语句
         * foreach的主要用在构建in条件中，它可以在SQL语句中进行迭代一个集合。
         * foreach元素的属性主要有 item，index，collection，open，separator，close。
         * collection 用$的方式，直接去先预加载传递过来的参数，避免in查询参数过多导致效率低下
         * item表示集合中每一个元素进行迭代时的别名
         * index指定一个名字，用于表示在迭代过程中，每次迭代到的位置，
         * open表示该语句以什么开始，
         * separator表示在每次进行迭代之间以什么符号作为分隔符,这里达成的效果是解析了in(a,b,c,...)
         * close表示以什么结束。*/
        if (matcher.find())
        {
            script = matcher.replaceAll("(<foreach collection=\"$1\" item=\"__item\" separator=\",\" >#{__item}</foreach>)");
        }
        /**完善新生成的语句*/
        script = "<script>" + script + "</script>";
        /**调用父类的方法,让其继续接下来的工作*/
        return super.createSqlSource(configuration, script, parameterType);
    }
}
